package com.octo4a.repository

import android.content.Context
import android.os.Environment
import com.octo4a.serial.VSPPty
import com.octo4a.utils.*
import com.octo4a.utils.preferences.MainPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import org.yaml.snakeyaml.Yaml
import java.io.*
import java.lang.reflect.Field
import java.util.regex.Matcher
import java.util.regex.Pattern
import kotlin.math.roundToInt

/**将文件从asstes文件夹复制到文件*/

/**服务器状态*/
enum class ServerStatus(val value: Int, val progress: Boolean=false) {
    InstallingBootstrap(0),
    InstallingDependencies(1),
    DownloadingOctoPrint(2),
    BootingUp(3, true),
    Running(4),
    ShuttingDown(3, true),
    Stopped(5),
    Corrupted(6)
}
/**附加*/
enum class ExtrasStatus {
    NotInstalled,
    Installing,
    Installed
}
/**Usb设备状态*/
data class UsbDeviceStatus(val isAttached: Boolean, val port: String = "")
/**服务器状态.获取安装进度*/
fun ServerStatus.getInstallationProgress(): Int {
    return ((value.toDouble() / 4) * 100).roundToInt()
}
/**服务器状态.是否安装完成*/
fun ServerStatus.isInstallationFinished(): Boolean {
    return value == ServerStatus.Running.value
}
/**OctoPrint处理程序存储库*/
interface OctoPrintHandlerRepository {
    /**服务器状态：状态流＜服务器状态＞*/
    val serverState: StateFlow<ServerStatus>
    /**octoPrint版本：状态流＜字符串＞*/
    val octoPrintVersion: StateFlow<String>
    /**usb设备状态：状态流＜usb设备状态＞*/
    val usbDeviceStatus: StateFlow<UsbDeviceStatus>
    /**注册的扩展名：状态流＜列表＜注册扩展＞＞*/
    val registeredExtensions: StateFlow<List<RegisteredExtension>>
    /**相机服务器状态：状态流<Boolean>*/
    val cameraServerStatus: StateFlow<Boolean>
    /**额外状态：状态流＜额外状态＞*/
    val extrasStatus: StateFlow<ExtrasStatus>
    /**开始安装*/
    suspend fun beginInstallation()
    /**开启OctoPrint*/
    fun startOctoPrint()
    /**停止OctoPrint*/
    fun stopOctoPrint()
    /**开启SSH*/
    fun startSSH()
    /**停止SSH*/
    fun stopSSH()
    /**安装附加组件*/
    fun installExtras()
    /**usb连接*/
    fun usbAttached(port: String)
    /**获取附加状态*/
    fun getExtrasStatus()
    /**usb分离*/
    fun usbDetached()
    /**重置SSH密码*/
    fun resetSSHPassword(password: String)
    /**获取配置值*/
    fun getConfigValue(value: String): String
    /**是否照相机服务器正在运行*/
    var isCameraServerRunning: Boolean
    /**是否已配置SSH*/
    val isSSHConfigured: Boolean
}
//OctoPrint处理程序存储库Impl
/***/
class OctoPrintHandlerRepositoryImpl(
    val context: Context,//上下文
    /**首选项：主首选项*/
    private val preferences: MainPreferences,
    /**记录器：记录器存储*/
    private val logger: LoggerRepository,
    /**引导存储库：引导存储库*/
    private val bootstrapRepository: BootstrapRepository,
    /**github仓库：github仓库*/
    private val githubRepository: GithubRepository,
    /**扩展存储库：扩展存储库*/
    private val extensionsRepository: ExtensionsRepository,
    /**fifo事件存储库：fifo事件存储库）：OctoPrint处理程序存储库*/
    private val fifoEventRepository: FIFOEventRepository) : OctoPrintHandlerRepository {
    private val externalStorageSymlinkPath = Environment.getExternalStorageDirectory().path + "/print3D"
    /**打印存储路径*/
    private val octoPrintStoragePath = "${context.getExternalFilesDir(null).absolutePath}/.print3D"
    private val octoStoragePath = "${context.getExternalFilesDir(null).absolutePath}"
    fun copyFromAssets( filesDir: String = "", item: String = "") {
        try {
            val fileOutputStream = FileOutputStream(File(filesDir, item))
            val bufferedInputStream = BufferedInputStream(context.assets.open(item))
            var len: Int
            val buf = ByteArray(1024)
            while (bufferedInputStream.read(buf).also { len = it } > 0) {
                fileOutputStream.write(buf, 0, len)
            }
            fileOutputStream.close()
            bufferedInputStream.close()

        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    /**配置文件路径*/
    private val configFile by lazy {
        File("$octoPrintStoragePath/config.yaml")
    }
    private val vspPty by lazy { VSPPty() }
    private val yaml by lazy { Yaml() }
    /**服务器状态*/
    private var _serverState = MutableStateFlow(ServerStatus.InstallingBootstrap)
    /**扩展状态*/
    private var _extensionsState = MutableStateFlow(listOf<RegisteredExtension>())
    /**Octoprint版本*/
    private var _octoPrintVersion = MutableStateFlow("...")
    /**usb设备状态*/
    private var _usbDeviceStatus = MutableStateFlow(UsbDeviceStatus(false))
    /**摄像机服务器状态*/
    private var _cameraServerStatus = MutableStateFlow(false)
    /**附加_状态*/
    private var _extrasStatus = MutableStateFlow(ExtrasStatus.NotInstalled)
    /**唤醒锁定*/
    private var wakeLock = Octo4aWakeLock(context, logger)
    /**进程*/
    private var octoPrintProcess: Process? = null
    /**fifo线程*/
    private var fifoThread: Thread? = null
    /**服务器状态*/
    override val serverState: StateFlow<ServerStatus> = _serverState
    /**已注册的扩展*/
    override val registeredExtensions: StateFlow<List<RegisteredExtension>> = _extensionsState
    /**Octoprint版本*/
    override val octoPrintVersion: StateFlow<String> = _octoPrintVersion
    /**usb设备状态*/
    override val usbDeviceStatus: StateFlow<UsbDeviceStatus> = _usbDeviceStatus
    /**摄像机服务器状态*/
    override val cameraServerStatus: StateFlow<Boolean> = _cameraServerStatus
    /**附加_状态*/
    override val extrasStatus: StateFlow<ExtrasStatus> = _extrasStatus
    /**照相机服务器是否运行*/
    override var isCameraServerRunning: Boolean
        /**摄像机服务器状态*/
        get() = _cameraServerStatus.value
        set(value) {
            _cameraServerStatus.value = value
        }
//开始安装
    override suspend fun beginInstallation() {
        /**输入输出调度*/
        withContext(Dispatchers.IO) {
            /**引导存储库。已安装引导程序*/
            if (!bootstrapRepository.isBootstrapInstalled) {
                logger.log { "未检测到引导，正在安装！" }
                /**服务器状态安装引导*/
                _serverState.emit(ServerStatus.InstallingBootstrap)//
                bootstrapRepository.apply {
                    setupBootstrap()
                }
                logger.log { "安装系统依赖项！" }
                /**安装依赖项*/
                _serverState.emit(ServerStatus.InstallingDependencies)
                bootstrapRepository.apply {
                    runCommand("apk add alpine-baselayout-data musl busybox busybox-binsh alpine-baselayout alpine-keys ca-certificates-bundle ssl_client  zlib apk-tools scanelf  musl-utils libc-utils ncurses-terminfo-base ncurses-libs").waitAndPrintOutput(logger)
                    runCommand("apk add readline  ca-certificates brotli-libs nghttp2-libs libcurl  openssh-keygen  sdl2  libxau libmd  libbsd libxdmcp libxcb libx11 pcre pcre2 ifupdown-ng libcap2 polkit").waitAndPrintOutput(logger)
                    runCommand("apk add aom-libs alsa-lib libexpat libbz2 libpng freetype fontconfig fribidi libffi libintl libblkid libmount pcre2 glib graphite2 harfbuzz libass libdav1d libdrm gmp nettle p11-kit").waitAndPrintOutput(logger)
                    runCommand("apk add libtasn1 libunistring gnutls lame opus libasyncns dbus-libs libltdl orc libgcc libogg libstdc++ flac-libs libvorbis libsndfile libgomp soxr speexdsp tdb-libs libpulse  cjson").waitAndPrintOutput(logger)
                    runCommand("apk add mbedtls librist libsrt libssh libtheora libjpeg-turbo v4l-utils-libs libxext libxfixes wayland-libs-client libva libvdpau vidstab libvpx libwebp x264-libs numactl x265-libs").waitAndPrintOutput(logger)
                    runCommand("apk add xvidcore libsodium libzmq ffmpeg-libs ffmpeg git py3-netifaces py3-olefile gdbm xz-libs mpdecimal sqlite-libs python3 libimagequant lcms2 openjpeg zstd-libs tiff py3-pillow").waitAndPrintOutput(logger)
                    runCommand("apk add py3-six py3-retrying py3-parsing py3-packaging py3-setuptools py3-pip py3-psutil py3-regex yaml py3-yaml libuv libwebsockets libwebsockets-evlib_uv json-c tzdata").waitAndPrintOutput(logger)
                    runCommand("apk add unzip binutils-avr isl25 mpfr4 mpc1 gcc-avr avr-libc musl-fts libelf confuse libusb libftdi1 avrdude binutils-arm-none-eabi binutils libmagic file libatomic gcc libstdc++-dev").waitAndPrintOutput(logger)
                    runCommand("apk add musl-dev libc-dev g++ make fortify-headers patch build-base can-utils gcc-arm-none-eabi linux-headers pkgconf libffi-dev libusb-dev ncurses-dev newlib-arm-none-eabi py3-cparser").waitAndPrintOutput(logger)
                    runCommand("apk add py3-cffi py3-greenlet python3-dev stm32flash libcrypto3 libssl3 openssl-dev nghttp2-dev zlib-dev brotli brotli-dev curl-dev libjpeg-turbo-dev jpeg-dev lmdb py3-lmdb py3-wheel").waitAndPrintOutput(logger)
                    runCommand("apk add nginx nginx-openrc libx11-dev libxrandr-dev dbus-dev dbus-glib-dev pkgconfig dbus-cpp-dev dbus-x11 dbus-libs dbus").waitAndPrintOutput(logger)
                    runCommand("wget https://gitee.com/xiaoh5001/klipperocto4a/raw/master/scripts/klipper-start.sh  -O /etc/init.d/klipper").waitFor()
                    runCommand("wget https://gitee.com/xiaoh5001/klipperocto4a/raw/master/scripts/nginxk_start.sh  -O /etc/init.d/nginxk").waitFor()
                    runCommand("wget https://gitee.com/xiaoh5001/klipperocto4a/raw/master/scripts/kliqj_start.sh  -O /etc/init.d/kliqj").waitFor()
                    runCommand("wget https://gitee.com/xiaoh5001/klipperocto4a/raw/master/scripts/moonraker-start.sh  -O /etc/init.d/moonraker").waitFor()
                    runCommand("wget https://gitee.com/xiaoh5001/klipperocto4a/raw/master/scripts/klipper.sh  -O /home/print3D/klipper.sh").waitFor()
                }

                logger.log { "安装klipper全家桶！" }
                /**下载OctoPrint*/
                _serverState.emit(ServerStatus.DownloadingOctoPrint)
                bootstrapRepository.apply {
                    runCommand("chmod +x /etc/init.d/kliqj").waitAndPrintOutput(logger)
                    runCommand("chmod 777 /etc/init.d/kliqj").waitAndPrintOutput(logger)
                    runCommand("chmod +x /home/print3D/klipper.sh").waitAndPrintOutput(logger)
                    runCommand("chmod 777 /home/print3D/klipper.sh").waitAndPrintOutput(logger)
                    runCommand("bash /home/print3D/klipper.sh").waitAndPrintOutput(logger)
                    runCommand("rm -rf /home/print3D/kiauh").waitAndPrintOutput(logger)
                    runCommand("rm -rf /home/print3D/klipper.sh").waitAndPrintOutput(logger)
                }
                logger.log { "安装启动程序！" }
                _serverState.emit(ServerStatus.Running)
                //copyFromAssets(octoStoragePath, "kliqj_start.sh")
                insertInitialConfig()
                logger.log { "启动程序安装完成！" }
            } else {
                getExtrasStatus()
                startOctoPrint()
                if (preferences.enableSSH) {
                    logger.log { "启用 ssh" }
                    startSSH()
                }
                extensionsRepository.startUpNecessaryExtensions()
            }
        }
    }
//获取附加组件状态
    override fun getExtrasStatus() {
        val file = File("/data/data/com.octo4a/files/bootstrap/bootstrap/usr/bin/gcc")

        if(file.exists()) {
            _extrasStatus.value = ExtrasStatus.Installed
        } else if (_extrasStatus.value != ExtrasStatus.Installing) {
            _extrasStatus.value = ExtrasStatus.NotInstalled
        }
    }
//安装附加组件
    override fun installExtras() {
        if (_extrasStatus.value == ExtrasStatus.NotInstalled) {
            Thread {
                _extrasStatus.value = ExtrasStatus.Installing
                bootstrapRepository.runCommand("curl -s https://raw.githubusercontent.com/feelfreelinux/octo4a/master/scripts/setup-plugin-extras.sh | bash -s").waitAndPrintOutput(logger)
                _extrasStatus.value = ExtrasStatus.Installed
            }.start()
        }
    }
//启动OctoPrint
    override fun startOctoPrint() {
        if (!isInstalledProperly) {
            _serverState.value = ServerStatus.Corrupted
            return
        }
        wakeLock.acquire()
        /**bootstrapRepository.run {
            #vspPty.createEventPipe()
        }*/

        _serverState.value = ServerStatus.BootingUp
        octoPrintProcess =bootstrapRepository.runCommand("rc-service kliqj restart")
        Thread {
            try {
                octoPrintProcess!!.inputStream.reader().forEachLine {
                    logger.log(this, LogType.OCTOPRINT) { it }
                    // TODO: Perhaps find a better way to handle it. Maybe some through plugin?
                    if (it.contains("#### 全家桶启动完成")) {
                        _serverState.value = ServerStatus.Running
                    }
                }
            } catch (e: Throwable) {
                stopOctoPrint()
            }
        }.start()
    }
//获取配置值
    override fun getConfigValue(value: String): String {
        return bootstrapRepository.runCommand("print3D config get $value", root = false)
            .getOutputAsString()
            .replace("\n", "")
            .removeSurrounding("'")
    }
//停止OctoPrint
    override fun stopOctoPrint() {
        wakeLock.remove()
        bootstrapRepository.runCommand("rc-service kliqj  stop")
        octoPrintProcess?.destroy()
        _serverState.value = ServerStatus.ShuttingDown
        Thread {
           while(!Thread.interrupted()) {
            Thread.sleep(500)
            if (octoPrintProcess?.isRunning() == false) {
                _serverState.value = ServerStatus.Stopped
                break;
            }
           }
        }.start()
        _serverState.value = ServerStatus.Stopped
    }
//重置SSH密码
    override fun resetSSHPassword(password: String) {
        bootstrapRepository.runCommand("echo \"$password\" > /root/.octoCredentials")
        bootstrapRepository.resetSSHPassword(password)
    }
//启动SSH
    override fun startSSH() {
        stopSSH()
        bootstrapRepository.runCommand("/usr/sbin/sshd -p ${preferences.sshPort}")
    }
//停止SSH
    override fun stopSSH() {
        // Kills ssh demon
        bootstrapRepository.runCommand("pkill sshd").waitAndPrintOutput(logger)
        logger.log(this) { "killed sshd" }
    }
//usb已附加
    override fun usbAttached(port: String) {
        _usbDeviceStatus.value = UsbDeviceStatus(true, port)
    }
//usb详细信息
    override fun usbDetached() {
        _usbDeviceStatus.value = UsbDeviceStatus(false)
    }
//插入初始配置
    private fun insertInitialConfig() {
        bootstrapRepository.ensureHomeDirectory()
        val map = getConfig()
        map["webcam"] = mapOf(
            "stream" to "http://${context.ipAddress}/mjpeg",
            "ffmpeg" to "/usr/bin/ffmpeg",
            "snapshot" to "http://localhost/snapshot"
        )
        map["serial"] = mapOf(
            "exclusive" to false,
            "additionalPorts" to listOf("/dev/ttyOcto4a")
            )
        map["server"] = mapOf("commands" to mapOf(
            "serverRestartCommand" to "echo \"{\\\"eventType\\\": \\\"restartServer\\\"}\" > /eventPipe",
            "systemShutdownCommand" to "echo \"{\\\"eventType\\\": \\\"stopServer\\\"}\" > /eventPipe"
        ))

        saveConfig(map)
    }
//获取配置
    private fun getConfig(): MutableMap<String, Any> { var output = emptyMap<String, Any>()
        if (configFile.exists()) {
            output = yaml.load(configFile.inputStream()) as Map<String, Any>
        } else {
            val file = File(octoPrintStoragePath)
            file.mkdirs()
            configFile.createNewFile()
        }

        return output.toMutableMap()
    }
//保存配置
    fun saveConfig(config: MutableMap<String, Any>) {
        val writer = FileWriter(configFile, false)

        yaml.dump(config, writer)
        writer.flush()
        writer.close()

        val backupFile = File("$octoPrintStoragePath/config.backup")
        backupFile.delete()
    }

    override val isSSHConfigured: Boolean
        get() = bootstrapRepository.isSSHConfigured

    // 验证安装
    val isInstalledProperly: Boolean
        get() = File("$octoStoragePath/extensions/klipper").exists()

}
